﻿namespace Learning.Controllers
{
    using System;
    using System.Collections.Generic;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Net;
    using System.Web;
    using System.Web.Mvc;
    using System.Web.UI;
    using Learning.Models;

    public class HomeController : Controller
    {
        private readonly FileRepository repository = new FileRepository();

        [HttpGet]
        public ActionResult Index()
        {
            var files = this.repository.Select();
            return View("Index", files);
        }

        [HttpGet]
        public ActionResult Add()
        {
            return View("Add");
        }

        [HttpPost]
        public ActionResult Create(HttpPostedFileBase file)
        {
            this.repository.Create(new FileModel()
            {
                Binary = GetBytes(file.InputStream),
                ContentType = file.ContentType,
                FileName = Path.GetFileName(file.FileName)
            });

            return RedirectToAction("Index");
        }

        [HttpGet]
        public ActionResult Edit(Guid id)
        {
            var file = this.repository.Select(id);
            return View("Edit", file);
        }

        [HttpPost]
        public ActionResult Update(Guid id, HttpPostedFileBase file)
        {
            this.repository.Update(new FileModel()
            {
                Id = id,
                Binary = GetBytes(file.InputStream),
                ContentType = file.ContentType,
                FileName = Path.GetFileName(file.FileName)
            });

            return RedirectToAction("Index");
        }

        [ActionName("Images")]
        [HttpGet]
        public ActionResult GetImage(Guid id)
        {
            var file = this.repository.Select(id);

            if (file == null)
            {
                return HttpNotFound();
            }

            var lastModified = file.Modified;
            var etag = file.Id.ToString();

            if (!HandleIfModifiedSince(lastModified) || !HandleIfNoneMatch(etag))
            {
                this.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
                this.HttpContext.Response.Cache.SetLastModified(lastModified.ToLocalTime());
                this.HttpContext.Response.Cache.SetETag(etag);

                return File(file.Binary, file.ContentType, file.FileName);
            }

            return null;
        }

        private bool HandleIfModifiedSince(DateTime lastModified)
        {
            var result = CheckIfModifiedSince(lastModified);

            if (result)
            {
                this.HttpContext.Response.ClearContent();

                this.HttpContext.Response.StatusCode = 304;
                this.HttpContext.Response.StatusDescription = "Not Modified";

                this.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
                this.HttpContext.Response.Cache.SetLastModified(lastModified.ToLocalTime());
            }

            return result;
        }

        private bool HandleIfNoneMatch(string etag)
        {
            var result = CheckIfNoneMatch(etag);

            if (result)
            {
                this.HttpContext.Response.ClearContent();

                this.HttpContext.Response.StatusCode = 304;
                this.HttpContext.Response.StatusDescription = "Not Modified";

                this.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
                this.HttpContext.Response.Cache.SetETag(etag);
            }

            return result;
        }

        private bool CheckIfModifiedSince(DateTime lastModified)
        {
            var lastModifiedHeader = this.HttpContext.Request.Headers["If-Modified-Since"];

            if (!string.IsNullOrEmpty(lastModifiedHeader))
            {
                // The HTTP last modified header does not include millisecond
                lastModified = new DateTime(lastModified.Year, lastModified.Month, lastModified.Day, lastModified.Hour, lastModified.Minute, lastModified.Second);

                DateTime clientLastModified;
                if (DateTime.TryParseExact(lastModifiedHeader, "r", CultureInfo.InvariantCulture, DateTimeStyles.None, out clientLastModified))
                {
                    if (clientLastModified == lastModified)
                    {
                        return true;
                    }
                }
            }

            return false;
        }

        private bool CheckIfNoneMatch(string etag)
        {
            var etagHeader = this.HttpContext.Request.Headers["If-None-Match"];

            if (!string.IsNullOrEmpty(etagHeader))
            {
                if (etagHeader == etag)
                {
                    return true;
                }
            }

            return false;
        }

        private static byte[] GetBytes(Stream stream)
        {
            var result = new byte[stream.Length];

            stream.Seek(0, SeekOrigin.Begin);

            var bytesRead = 0;
            var totalBytesRead = 0;
            do
            {
                bytesRead = stream.Read(result, totalBytesRead, 1000);
                totalBytesRead += bytesRead;
            } while (bytesRead == 1000);

            return result;
        }
    }
}
